import os
import tempfile
import uuid
import zipfile
import shutil
from typing import List, Optional
from fastapi import APIRouter, UploadFile, File, HTTPException, Query
from fastapi.responses import FileResponse
from pdf2image import convert_from_path, convert_from_bytes
from pdf2image.exceptions import PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError

# 创建路由器
router = APIRouter(
    prefix="/pdf2image",
    tags=["PDF转图片"],
    responses={404: {"description": "Not found"}},
)

# 默认目录
DEFAULT_INPUT_DIR = "input_files"
DEFAULT_OUTPUT_DIR = "uploads"#"output_files"
DEFAULT_DPI = 200

def ensure_directory_exists(directory_path):
    """确保目录存在，如果不存在则创建"""
    # 检查路径是否为空字符串或None
    if not directory_path:
        return  # 如果是空字符串或None，直接返回，不创建目录
        
    if not os.path.exists(directory_path):
        os.makedirs(directory_path)

def convert_pdf_to_images(
    pdf_path, 
    output_dir, 
    dpi=DEFAULT_DPI, 
    fmt="png", 
    first_page=None, 
    last_page=None, 
    grayscale=False,
    use_pdftocairo=True
):
    """
    将PDF文件转换为图片
    
    Args:
        pdf_path: PDF文件路径
        output_dir: 输出目录路径
        dpi: 图片DPI，默认为200
        fmt: 图片格式，默认为png
        first_page: 起始页码
        last_page: 结束页码
        grayscale: 是否转换为灰度图
        use_pdftocairo: 是否使用pdftocairo而不是pdftoppm
        
    Returns:
        生成的图片路径列表
    """
    # 确保输出目录存在
    ensure_directory_exists(output_dir)
    
    # 生成唯一的输出文件名前缀
    file_basename = os.path.basename(pdf_path)
    output_filename_prefix = f"output_{os.path.splitext(file_basename)[0]}_{uuid.uuid4().hex[:8]}"
    
    try:
        # 使用pdf2image库转换
        images = convert_from_path(
            pdf_path=pdf_path,
            dpi=dpi,
            output_folder=output_dir,
            fmt=fmt,
            first_page=first_page,
            last_page=last_page,
            grayscale=grayscale,
            use_pdftocairo=use_pdftocairo,
            output_file=output_filename_prefix,
            paths_only=True  # 返回文件路径而不是PIL Image对象，以防止内存溢出
        )
        
        return images
    except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e:
        raise ValueError(f"PDF处理错误: {str(e)}")
    except Exception as e:
        raise ValueError(f"转换PDF时出错: {str(e)}")

def convert_pdf_bytes_to_images(
    pdf_bytes, 
    output_dir, 
    dpi=DEFAULT_DPI, 
    fmt="png", 
    first_page=None, 
    last_page=None, 
    grayscale=False,
    use_pdftocairo=True
):
    """
    将PDF字节内容转换为图片
    
    Args:
        pdf_bytes: PDF字节内容
        output_dir: 输出目录路径
        dpi: 图片DPI，默认为200
        fmt: 图片格式，默认为png
        first_page: 起始页码
        last_page: 结束页码
        grayscale: 是否转换为灰度图
        use_pdftocairo: 是否使用pdftocairo而不是pdftoppm
        
    Returns:
        生成的图片路径列表
    """
    # 确保输出目录存在
    ensure_directory_exists(output_dir)
    
    # 生成唯一的输出文件名前缀
    output_filename_prefix = f"output_pdf_{uuid.uuid4().hex[:8]}"
    
    try:
        # 使用pdf2image库转换
        images = convert_from_bytes(
            pdf_file=pdf_bytes,
            dpi=dpi,
            output_folder=output_dir,
            fmt=fmt,
            first_page=first_page,
            last_page=last_page,
            grayscale=grayscale,
            use_pdftocairo=use_pdftocairo,
            output_file=output_filename_prefix,
            paths_only=True  # 返回文件路径而不是PIL Image对象，以防止内存溢出
        )
        
        return images
    except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e:
        raise ValueError(f"PDF处理错误: {str(e)}")
    except Exception as e:
        raise ValueError(f"转换PDF时出错: {str(e)}")

def create_zip_file(image_paths, output_dir, zip_filename=None):
    """
    将多个图片文件打包为一个ZIP文件
    
    Args:
        image_paths: 图片文件路径列表
        output_dir: 输出目录路径
        zip_filename: ZIP文件名，默认为自动生成
        
    Returns:
        生成的ZIP文件路径
    """
    if not zip_filename:
        zip_filename = f"pdf_images_{uuid.uuid4().hex[:8]}.zip"
    
    zip_path = os.path.join(output_dir, zip_filename)
    
    with zipfile.ZipFile(zip_path, 'w') as zipf:
        for img_path in image_paths:
            # 添加到zip文件，只使用文件名而非完整路径
            zipf.write(img_path, os.path.basename(img_path))
    
    return zip_path

def convert_pdf_to_folder(
    pdf_path, 
    base_output_dir, 
    dpi=DEFAULT_DPI, 
    fmt="png", 
    first_page=None, 
    last_page=None, 
    grayscale=False,
    use_pdftocairo=True
):
    """
    将PDF文件转换为图片，并将图片放入专门的文件夹中
    
    Args:
        pdf_path: PDF文件路径
        base_output_dir: 基础输出目录路径
        dpi: 图片DPI，默认为200
        fmt: 图片格式，默认为png
        first_page: 起始页码
        last_page: 结束页码
        grayscale: 是否转换为灰度图
        use_pdftocairo: 是否使用pdftocairo而不是pdftoppm
        
    Returns:
        包含输出文件夹路径和生成的图片路径的字典
    """
    # 生成唯一文件夹名
    folder_uuid = uuid.uuid4().hex
    output_folder = os.path.join(base_output_dir, folder_uuid)
    images_folder = os.path.join(output_folder, "images")
    
    # 确保输出目录和images子目录存在
    ensure_directory_exists(output_folder)
    ensure_directory_exists(images_folder)
    
    # 生成唯一的输出文件名前缀
    file_basename = os.path.basename(pdf_path)
    output_filename_prefix = f"page_{os.path.splitext(file_basename)[0]}"
    
    try:
        # 使用pdf2image库转换
        images = convert_from_path(
            pdf_path=pdf_path,
            dpi=dpi,
            output_folder=images_folder,
            fmt=fmt,
            first_page=first_page,
            last_page=last_page,
            grayscale=grayscale,
            use_pdftocairo=use_pdftocairo,
            output_file=output_filename_prefix,
            paths_only=True  # 返回文件路径而不是PIL Image对象，以防止内存溢出
        )
        
        # 将原始PDF文件复制到输出文件夹
        pdf_dest_path = os.path.join(output_folder, os.path.basename(pdf_path))
        shutil.copy2(pdf_path, pdf_dest_path)
        
        return {
            "folder_path": output_folder,
            "folder_uuid": folder_uuid,
            "images_folder": images_folder,
            "image_paths": images,
            "pdf_path": pdf_dest_path
        }
    except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e:
        raise ValueError(f"PDF处理错误: {str(e)}")
    except Exception as e:
        raise ValueError(f"转换PDF时出错: {str(e)}")

# 添加路由器端点
@router.post("/convert")
async def convert_pdf_to_images_api(
    file: UploadFile = File(...),
    input_dir: Optional[str] = Query(DEFAULT_INPUT_DIR, description="输入文件目录"),
    output_dir: Optional[str] = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录"),
    dpi: Optional[str] = Query(str(DEFAULT_DPI), description="图片DPI"),
    fmt: Optional[str] = Query("png", description="图片格式 (png, jpg, tiff)"),
    first_page: Optional[str] = Query(None, description="起始页码"),
    last_page: Optional[str] = Query(None, description="结束页码"),
    grayscale: Optional[str] = Query("false", description="是否转换为灰度图"),
    use_pdftocairo: Optional[str] = Query("true", description="是否使用pdftocairo转换")
):
    """
    将PDF文件转换为图片
    
    - **file**: 要上传的PDF文件
    - **input_dir**: 输入文件保存目录，默认为'input_files'
    - **output_dir**: 输出文件保存目录，默认为'output_files'
    - **dpi**: 图片DPI，默认为200
    - **fmt**: 图片格式，可选png、jpg、tiff，默认为png
    - **first_page**: 起始页码，默认为None表示第一页
    - **last_page**: 结束页码，默认为None表示最后一页
    - **grayscale**: 是否转换为灰度图，默认为false
    - **use_pdftocairo**: 是否使用pdftocairo，通常比pdftoppm更快，默认为true
    
    注意：此接口使用pdf2image库，它是对pdftoppm和pdftocairo的Python封装，
    提供高质量的PDF到图片转换功能。支持将PDF的每一页转换为单独的图片文件。
    """
    # 检查文件类型
    if not file.filename.lower().endswith('.pdf'):
        raise HTTPException(status_code=400, detail="只接受PDF文件")
    
    # 处理默认目录
    if not input_dir or input_dir.strip() == "":
        input_dir = DEFAULT_INPUT_DIR
    
    if not output_dir or output_dir.strip() == "":
        output_dir = DEFAULT_OUTPUT_DIR
    
    # 处理默认图片格式
    if not fmt or fmt.strip() == "":
        fmt = "png"
    
    # 验证图片格式
    valid_formats = ["png", "jpg", "jpeg", "tiff"]
    if fmt.lower() not in valid_formats:
        raise HTTPException(status_code=400, detail=f"不支持的图片格式，请使用: {', '.join(valid_formats)}")
    
    # 确保目录存在
    ensure_directory_exists(input_dir)
    ensure_directory_exists(output_dir)
    
    # 处理整数参数
    dpi_value = DEFAULT_DPI
    if dpi and dpi.strip():
        try:
            dpi_value = int(dpi)
        except ValueError:
            raise HTTPException(status_code=400, detail="DPI参数必须是整数")
    
    first_page_value = None
    if first_page and first_page.strip():
        try:
            first_page_value = int(first_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="起始页码参数必须是整数")
    
    last_page_value = None
    if last_page and last_page.strip():
        try:
            last_page_value = int(last_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="结束页码参数必须是整数")
    
    # 处理布尔参数
    grayscale_value = False
    if grayscale:
        grayscale_lower = grayscale.lower().strip()
        if grayscale_lower in ["true", "1", "yes", "y"]:
            grayscale_value = True
        elif grayscale_lower in ["false", "0", "no", "n", ""]:
            grayscale_value = False
        else:
            raise HTTPException(status_code=400, detail="grayscale参数必须是布尔值")
    
    use_pdftocairo_value = True
    if use_pdftocairo:
        pdftocairo_lower = use_pdftocairo.lower().strip()
        if pdftocairo_lower in ["true", "1", "yes", "y"]:
            use_pdftocairo_value = True
        elif pdftocairo_lower in ["false", "0", "no", "n", ""]:
            use_pdftocairo_value = False
        else:
            raise HTTPException(status_code=400, detail="use_pdftocairo参数必须是布尔值")
    
    # 保存上传的文件
    file_path = os.path.join(input_dir, file.filename)
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())
    
    try:
        # 处理PDF文件
        image_paths = convert_pdf_to_images(
            pdf_path=file_path,
            output_dir=output_dir,
            dpi=dpi_value,
            fmt=fmt,
            first_page=first_page_value,
            last_page=last_page_value,
            grayscale=grayscale_value,
            use_pdftocairo=use_pdftocairo_value
        )
        
        # 如果生成了多张图片，创建一个ZIP文件
        if len(image_paths) > 1:
            zip_path = create_zip_file(image_paths, output_dir)
            file_name = os.path.basename(zip_path)
            download_url = f"/outputs/{file_name}"
            
            return {
                "success": True,
                "message": f"PDF转换成功，共生成{len(image_paths)}张图片",
                "images_count": len(image_paths),
                "file_path": zip_path,
                "download_url": download_url,
                "image_paths": [os.path.basename(path) for path in image_paths]
            }
        elif len(image_paths) == 1:
            # 只有一张图片时，直接返回图片信息
            file_name = os.path.basename(image_paths[0])
            download_url = f"/outputs/{file_name}"
            
            return {
                "success": True,
                "message": "PDF转换成功，生成了1张图片",
                "images_count": 1,
                "file_path": image_paths[0],
                "download_url": download_url
            }
        else:
            raise HTTPException(status_code=500, detail="未能生成任何图片")
    except ValueError as e:
        raise HTTPException(status_code=500, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"处理文件时出错: {str(e)}")

@router.get("/download/{zip_filename}")
async def download_zip_file(
    zip_filename: str,
    output_dir: str = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录")
):
    """
    下载ZIP文件
    
    - **zip_filename**: ZIP文件名
    - **output_dir**: 输出文件目录，默认为'output_files'
    """
    file_path = os.path.join(output_dir, zip_filename)
    
    if not os.path.exists(file_path):
        raise HTTPException(status_code=404, detail="文件不存在")
    
    return FileResponse(
        path=file_path, 
        filename=zip_filename, 
        media_type="application/zip"
    )

@router.post("/convert2folder")
async def convert_pdf_to_folder_api(
    file: UploadFile = File(...),
    input_dir: Optional[str] = Query(DEFAULT_INPUT_DIR, description="输入文件目录"),
    output_dir: Optional[str] = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录"),
    dpi: Optional[str] = Query(str(DEFAULT_DPI), description="图片DPI"),
    fmt: Optional[str] = Query("png", description="图片格式 (png, jpg, tiff)"),
    first_page: Optional[str] = Query(None, description="起始页码"),
    last_page: Optional[str] = Query(None, description="结束页码"),
    grayscale: Optional[str] = Query("false", description="是否转换为灰度图"),
    use_pdftocairo: Optional[str] = Query("true", description="是否使用pdftocairo转换")
):
    """
    将PDF文件转换为图片并存放到专门的文件夹中
    
    - **file**: 要上传的PDF文件
    - **input_dir**: 输入文件保存目录，默认为'input_files'
    - **output_dir**: 输出文件保存目录，默认为'output_files'
    - **dpi**: 图片DPI，默认为200
    - **fmt**: 图片格式，可选png、jpg、tiff，默认为png
    - **first_page**: 起始页码，默认为None表示第一页
    - **last_page**: 结束页码，默认为None表示最后一页
    - **grayscale**: 是否转换为灰度图，默认为false
    - **use_pdftocairo**: 是否使用pdftocairo，通常比pdftoppm更快，默认为true
    
    注意：此接口将为每个PDF创建一个唯一的文件夹，将PDF文件和转换后的图片文件（存放在images子文件夹中）一起返回。
    所有返回的路径均为绝对路径。
    """
    # 检查文件类型
    if not file.filename.lower().endswith('.pdf'):
        raise HTTPException(status_code=400, detail="只接受PDF文件")
    
    # 处理默认目录
    if not input_dir or input_dir.strip() == "":
        input_dir = DEFAULT_INPUT_DIR
    
    if not output_dir or output_dir.strip() == "":
        output_dir = DEFAULT_OUTPUT_DIR
    
    # 转换为绝对路径
    input_dir = os.path.abspath(input_dir)
    output_dir = os.path.abspath(output_dir)
    
    # 处理默认图片格式
    if not fmt or fmt.strip() == "":
        fmt = "png"
    
    # 验证图片格式
    valid_formats = ["png", "jpg", "jpeg", "tiff"]
    if fmt.lower() not in valid_formats:
        raise HTTPException(status_code=400, detail=f"不支持的图片格式，请使用: {', '.join(valid_formats)}")
    
    # 确保目录存在
    ensure_directory_exists(input_dir)
    ensure_directory_exists(output_dir)
    
    # 处理整数参数
    dpi_value = DEFAULT_DPI
    if dpi and dpi.strip():
        try:
            dpi_value = int(dpi)
        except ValueError:
            raise HTTPException(status_code=400, detail="DPI参数必须是整数")
    
    first_page_value = None
    if first_page and first_page.strip():
        try:
            first_page_value = int(first_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="起始页码参数必须是整数")
    
    last_page_value = None
    if last_page and last_page.strip():
        try:
            last_page_value = int(last_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="结束页码参数必须是整数")
    
    # 处理布尔参数
    grayscale_value = False
    if grayscale:
        grayscale_lower = grayscale.lower().strip()
        if grayscale_lower in ["true", "1", "yes", "y"]:
            grayscale_value = True
        elif grayscale_lower in ["false", "0", "no", "n", ""]:
            grayscale_value = False
        else:
            raise HTTPException(status_code=400, detail="grayscale参数必须是布尔值")
    
    use_pdftocairo_value = True
    if use_pdftocairo:
        pdftocairo_lower = use_pdftocairo.lower().strip()
        if pdftocairo_lower in ["true", "1", "yes", "y"]:
            use_pdftocairo_value = True
        elif pdftocairo_lower in ["false", "0", "no", "n", ""]:
            use_pdftocairo_value = False
        else:
            raise HTTPException(status_code=400, detail="use_pdftocairo参数必须是布尔值")
    
    # 保存上传的文件
    file_path = os.path.join(input_dir, file.filename)
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())
    
    try:
        # 处理PDF文件并保存到专门的文件夹中
        result = convert_pdf_to_folder(
            pdf_path=file_path,
            base_output_dir=output_dir,
            dpi=dpi_value,
            fmt=fmt,
            first_page=first_page_value,
            last_page=last_page_value,
            grayscale=grayscale_value,
            use_pdftocairo=use_pdftocairo_value
        )
        
        # 确保所有路径都是绝对路径
        folder_path = os.path.abspath(result["folder_path"])
        images_folder = os.path.abspath(result["images_folder"])
        pdf_path = os.path.abspath(result["pdf_path"])
        
        # 将图片路径列表也转换为绝对路径
        image_paths_abs = [os.path.abspath(path) for path in result["image_paths"]]
        
        # 构建URL路径 (保留相对URL以便于HTTP访问)
        folder_uuid = result["folder_uuid"]
        folder_url = f"/pdf2image/folder/{folder_uuid}"
        images_folder_relative = os.path.relpath(images_folder, output_dir)
        images_folder_url = f"/outputs/{images_folder_relative}"
        
        # 获取服务器根目录
        server_root = os.path.abspath(os.getcwd())
        
        # 返回处理结果，所有路径都是绝对路径
        return {
            "success": True,
            "message": f"PDF转换成功，共生成{len(image_paths_abs)}张图片，保存在专门的文件夹中",
            "folder_uuid": folder_uuid,
            "folder_path": folder_path,
            "folder_url": folder_url,
            "server_root": server_root,
            "images_count": len(image_paths_abs),
            "images_folder": images_folder,
            "images_folder_url": images_folder_url,
            "image_paths": [os.path.abspath(path) for path in result["image_paths"]],
            "image_names": [os.path.basename(path) for path in result["image_paths"]],
            "original_pdf": os.path.abspath(file_path),
            "original_pdf_name": os.path.basename(file.filename)
        }
    except ValueError as e:
        raise HTTPException(status_code=500, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"处理文件时出错: {str(e)}")

@router.get("/folder/{folder_uuid}")
async def get_folder_info(
    folder_uuid: str,
    output_dir: str = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录")
):
    """
    获取指定文件夹的信息
    
    - **folder_uuid**: 文件夹UUID
    - **output_dir**: 输出文件目录，默认为'output_files'
    
    注意：返回的所有路径均为绝对路径。
    """
    # 转换为绝对路径
    output_dir = os.path.abspath(output_dir)
    folder_path = os.path.join(output_dir, folder_uuid)
    
    if not os.path.exists(folder_path):
        raise HTTPException(status_code=404, detail="文件夹不存在")
    
    images_folder = os.path.join(folder_path, "images")
    
    # 获取文件夹内容
    folder_files = []
    if os.path.exists(folder_path):
        folder_files = [f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))]
    
    image_files = []
    if os.path.exists(images_folder):
        image_files = [f for f in os.listdir(images_folder) if os.path.isfile(os.path.join(images_folder, f))]
    
    # 构建URL
    folder_url = f"/pdf2image/folder/{folder_uuid}"
    images_folder_relative = os.path.relpath(images_folder, output_dir)
    images_folder_url = f"/outputs/{images_folder_relative}"
    
    # 构建绝对路径的文件列表
    folder_files_abs = [os.path.abspath(os.path.join(folder_path, f)) for f in folder_files]
    image_files_abs = [os.path.abspath(os.path.join(images_folder, f)) for f in image_files]
    
    # 获取服务器根目录
    server_root = os.path.abspath(os.getcwd())
    
    return {
        "folder_uuid": folder_uuid,
        "folder_path": os.path.abspath(folder_path),
        "folder_url": folder_url,
        "server_root": server_root,
        "folder_files": folder_files_abs,
        "folder_file_names": folder_files,
        "images_folder": os.path.abspath(images_folder),
        "images_folder_url": images_folder_url,
        "image_files": image_files_abs,
        "image_file_names": image_files
    }
